/*
 * @features: 功能
 * @description: 说明
 * @Date: 2020-12-24 16:41:54
 * @Author: judu233(769471424@qq.com)
 * @LastEditTime: 2021-09-12 15:16:48
 * @LastEditors: judu233
 */

import CPiece from "./CPiece";

const { ccclass, property } = cc._decorator;
const MASK_TYPE = `maskType`;
enum E_PieceMaskType {
    ancho = `ancho`,
    bordA = `bordA`,
    bordB = `bordB`,
    centerA = `centerA`,
    centerB = `centerB`,
}
enum E_PiecePosType {
    left_up = `left_up`,
    right_up = `right_up`,
    right_down = `right_down`,
    left_down = `left_down`,
    up = `up`,
    right = `right`,
    down = `down`,
    left = `left`,
    center = `center`,
}

interface I_PieceInfo {
    maskSf: cc.SpriteFrame;
    pieceType: E_PieceMaskType;
    rotation: number;
    startPos: cc.Vec3;
    contentOffset: cc.Vec3;
    maskSize: cc.Size;
}
/**
 * 预制体碎片控制
 */
@ccclass
export default class CPieceMgr extends cc.Component {
    /**是否是规则拼图 */
    @property({ displayName: `规则拼图` })
    isRulse = true;

    @property({
        type: cc.Node,
        displayName: `容器`
    })
    contentNode: cc.Node = null;

    @property({
        type: cc.SpriteFrame,
        displayName: `显示的图片`
    })
    contentSf: cc.SpriteFrame = null;

    @property({
        type: cc.Node,
        displayName: `放置临时拖拽节点`
    })
    tempDragNode: cc.Node = null;

    @property({
        type: cc.Node,
        displayName: `放置临时容器`
    })
    tempContent: cc.Node = null;

    @property({
        displayName: `拼图大小`,
        visible(this: CPieceMgr) { return this.isRulse; }
    })
    bgSize: cc.Size = cc.size(604, 910);

    @property({
        displayName: `格子数量`,
        visible(this: CPieceMgr) { return this.isRulse; }
    })
    gradCount: cc.Size = cc.size(2, 3);

    /**角落mask */
    @property({
        type: cc.SpriteFrame,
        displayName: `角落mask`,
        visible(this: CPieceMgr) { return !this.isRulse; }
    })
    private anchoMask: cc.SpriteFrame = null;
    /**边缘mask */
    @property({
        type: cc.SpriteFrame,
        displayName: `边缘maskA`,
        visible(this: CPieceMgr) { return !this.isRulse; }
    })
    private borderMaskA: cc.SpriteFrame = null;
    @property({
        type: cc.SpriteFrame,
        displayName: `边缘maskB`,
        visible(this: CPieceMgr) { return !this.isRulse; }
    })
    private borderMaskB: cc.SpriteFrame = null;
    /**中心Mask */
    @property({
        type: cc.SpriteFrame,
        displayName: `中心Mask`,
        visible(this: CPieceMgr) { return !this.isRulse; }
    })
    centerMask: cc.SpriteFrame = null;

    /**碎片的倍数--1x1为4x4, 2x2=6x6 3x3=8x8 */
    @property({
        displayName: `不规则碎片的倍数`,
        tooltip: `碎片的倍数--1x1为4x4, 2x2=6x6 3x3=8x8`,
        visible(this: CPieceMgr) { return !this.isRulse; }
    })
    multipleSize = cc.size(3, 3);

    @property({
        displayName: `不规则图片缩放倍率`,
        visible(this: CPieceMgr) { return !this.isRulse; }
    })
    pieceScale = 1;

    @property({
        displayName: `根据最近位置查找交换`,
        tooltip: `开启后拖拽结束后根据最近位置交换设置碎片，否则只有拖拽到正确后才赋值`
    })
    isFindAround = false;

    @property({
        displayName: `拼图完成距离值`,
        tooltip: `拖拽到目标拼图处时检查距离目标时的判断距离`
    })
    findDistence = 75;

    @property({
        displayName: `开启测试`,
    })
    isOpenTest = false;

    /**测试节点 */
    @property({
        type: cc.Node,
        displayName: `测试锚点的单色`,
        visible(this: CPieceMgr) { return this.isOpenTest; }
    })
    testAnchoNode: cc.Node = null;

    /**格子尺寸 */
    cellSize: cc.Size = cc.size(0, 0);
    /**移动速度 */
    moveSpeed: number = 0.3;

    /**碎片去掉凸出部分的宽高的方形原始尺寸 */
    private pieceBulgeWidth: number;
    /**碎片偏移尺寸 */
    private pieceOffset: number;
    /**存放可以拖拽的碎片节点map */
    private pieceDragMap: Map<string, CPiece> = new Map();
    /**存放正确碎片的位置map */
    private rightPieceMap: Map<string, CPiece> = new Map();
    /**存放碎片开始位置的map */
    private pieceStartPosMap: Map<string, cc.Vec3> = new Map();
    /**存放临时容器的位置map */
    private tempPosMap: Map<string, cc.Vec3> = new Map();
    /**存放信息 */
    private pieceInfoMap: Map<string, I_PieceInfo> = new Map();

    /**偏移 */
    private _offset: number = 0;
    set offset(offset: number) { this._offset = offset; }
    get offset(): number { return this._offset; }

    private tempScale = 0.3;

    start() {
        this.init();
    }

    init() {
        if (!this.isRulse) {
            //计算不规则容器行列
            this.gradCount = cc.size(this.multipleSize.width * 2 + 2, this.multipleSize.height * 2 + 2);

            //计算非凸出部分和偏移
            let anchoSize = this.anchoMask.getOriginalSize();
            this.pieceBulgeWidth = Math.min(anchoSize.width, anchoSize.height) * this.pieceScale;
            this.pieceOffset = (Math.max(anchoSize.width, anchoSize.height) * this.pieceScale - this.pieceBulgeWidth);

            //计算容器大小
            this.bgSize = cc.size(this.pieceBulgeWidth * this.gradCount.width, this.pieceBulgeWidth * this.gradCount.height);
            this.contentNode.setContentSize(this.bgSize);
        } else  //计算格子大小
            this.cellSize = cc.size(this.bgSize.width / this.gradCount.width, this.bgSize.height / this.gradCount.height);

        //创建
        let count: number = 0;
        let offset: number = 0;
        for (let i = 0; i < this.gradCount.height; i++) {
            for (let j = 0; j < this.gradCount.width; j++) {
                let name = this.getName(i, j);
                let maskInfo = this.getMaskInfo(i, j);
                let { startPos, pieceType, posType } = maskInfo;
                this.pieceInfoMap.set(name, maskInfo);

                //测试
                if (!this.isRulse && this.testPos(pieceType, posType)) continue;

                //创建碎片节点
                let touchPiece = this.createPiece(name, maskInfo);
                let piece = touchPiece.getComponent(CPiece);

                //正确的位置
                let rightPiece = cc.instantiate(touchPiece).getComponent(CPiece);
                this.contentNode.addChild(rightPiece.node);
                this.rightPieceMap.set(name, rightPiece);
                rightPiece.node.opacity = 100;
                // rightPiece.node.active = false;

                //添加到临时节点
                //计算每个碎片的实际宽度,高度
                let tempPos = this.contentNode.convertToNodeSpaceAR(this.tempContent.convertToWorldSpaceAR(cc.v3(count * 50, 0)));
                // pieceNode.rotation = 0;
                this.tempPosMap.set(name, tempPos);

                touchPiece.scale = this.tempScale;
                touchPiece.setPosition(tempPos);
                this.contentNode.addChild(touchPiece);

                //
                piece.init(this.cellSize, cc.v3(i, j), count);

                //存入
                this.pieceStartPosMap.set(name, startPos);
                this.pieceDragMap.set(name, piece);

                let width = touchPiece.width * touchPiece.scale;
                offset = offset <= 0 ? width : offset;
                count++;
            }
        }
        // this.contentNode.scale = 0.5;
        // this.randomPosition();
    }

    private getMaskInfo(i: number, j: number) {
        let startPos: cc.Vec3;
        let isLeftUp = (i == 0 && j == 0);
        let isRightUp = (i == 0 && j == this.gradCount.width - 1);
        let isLeftDown = (j == 0 && i == this.gradCount.height - 1);
        let isRightDown = (j == this.gradCount.width - 1 && i == this.gradCount.height - 1);
        let isLeft = (i > 0 && i < this.gradCount.height - 1 && j == 0);
        let isUp = (j > 0 && j < this.gradCount.width - 1 && i == 0);
        let isRight = (i > 0 && i < this.gradCount.height - 1 && j == this.gradCount.width - 1);
        let isDown = (j > 0 && j < this.gradCount.width - 1 && i == this.gradCount.height - 1);
        let rowIsModeA = i % 2 != 0;
        let colIsModeA = j % 2 != 0;
        let pieceType = E_PieceMaskType.ancho, maskSf = this.anchoMask, posType: E_PiecePosType,
            rotation = 0, maskOffset = cc.v3(0, 0), contentOffset = cc.v3(0, 0);
        let maskSize = maskSf.getOriginalSize();
        maskSize = cc.size(maskSize.width * this.pieceScale, maskSize.height * this.pieceScale);

        if (this.isRulse) {
            startPos = cc.v3(
                this.cellSize.width / 2 + this.cellSize.width * j,
                - (this.cellSize.height / 2 + this.cellSize.height * i),
            );
            maskSize = this.cellSize;
            contentOffset = startPos.clone().neg();
            return { startPos, pieceType, maskSf, posType, rotation, maskOffset, contentOffset, maskSize };
        } else {
            startPos = cc.v3(this.pieceBulgeWidth * j, -(this.pieceBulgeWidth * i));
        }

        //不规则
        {
            if (isLeftUp) {
                rotation = 0;
                posType = E_PiecePosType.left_up;
            } else if (isLeftDown) {
                posType = E_PiecePosType.left_down;
                rotation = 270;
                maskOffset = cc.v3(0, -maskSize.height + this.pieceOffset);
                contentOffset = cc.v3(maskSize.width * (i + 1), 0);
            } else if (isRightUp) {
                posType = E_PiecePosType.right_up;
                rotation = 90;
                maskOffset = cc.v3(maskSize.width, 0);
                contentOffset = cc.v3(0, -maskSize.width * (j + 1));
            } else if (isRightDown) {
                posType = E_PiecePosType.right_down;
                rotation = 180;
                maskOffset = cc.v3(maskSize.width, -maskSize.height + this.pieceOffset);
                contentOffset = cc.v3(maskSize.width * (j + 1), -maskSize.width * (i + 1));
            } else if (isLeft || isRight) {
                if (isLeft) {
                    rotation = 270;
                    posType = E_PiecePosType.left;
                    maskSf = !rowIsModeA ? this.borderMaskA : this.borderMaskB;
                    pieceType = !rowIsModeA ? E_PieceMaskType.bordA : E_PieceMaskType.bordB;
                    maskOffset = rowIsModeA ? cc.v3(0, -maskSize.height + this.pieceOffset) : cc.v3(0, -maskSize.height);
                    contentOffset = rowIsModeA ? cc.v3(maskSize.width * (i + 1), 0) : cc.v3(maskSize.width * (i + 1) + this.pieceOffset, 0)
                } else {
                    rotation = 90;
                    posType = E_PiecePosType.right;
                    maskSf = rowIsModeA ? this.borderMaskA : this.borderMaskB;
                    pieceType = rowIsModeA ? E_PieceMaskType.bordA : E_PieceMaskType.bordB;
                    maskOffset = !rowIsModeA ? cc.v3(maskSize.width, 0) : cc.v3(maskSize.width, this.pieceOffset);
                    contentOffset = !rowIsModeA ? cc.v3(-maskSize.width * i, -maskSize.width * (j + 1)) : cc.v3(-maskSize.width * i + this.pieceOffset, -maskSize.width * (j + 1))
                }
            } else if (isUp || isDown) {
                if (isUp) {
                    rotation = 0;
                    posType = E_PiecePosType.up;
                    maskSf = colIsModeA ? this.borderMaskA : this.borderMaskB;
                    pieceType = colIsModeA ? E_PieceMaskType.bordA : E_PieceMaskType.bordB;
                    maskOffset = colIsModeA ? cc.v3(-this.pieceOffset, 0) : cc.v3(0, 0);
                    contentOffset = colIsModeA ? cc.v3(- maskSize.width * j + this.pieceOffset, 0) : cc.v3(-maskSize.width * j, 0);
                } else {
                    rotation = 180;
                    posType = E_PiecePosType.down;
                    maskSf = !colIsModeA ? this.borderMaskA : this.borderMaskB;
                    pieceType = !colIsModeA ? E_PieceMaskType.bordA : E_PieceMaskType.bordB;
                    maskOffset = !colIsModeA ?
                        cc.v3(maskSize.width + this.pieceOffset, -maskSize.height + this.pieceOffset) :
                        cc.v3(maskSize.width, -maskSize.height + this.pieceOffset);
                    contentOffset = !colIsModeA ?
                        cc.v3(maskSize.width * (j + 1) + this.pieceOffset, -maskSize.width * (i + 1)) :
                        cc.v3(maskSize.width * (j + 1), -maskSize.width * (i + 1));
                }
            } else {
                if ((colIsModeA && !rowIsModeA) || (!colIsModeA && rowIsModeA)) {
                    rotation = 90;
                    pieceType = E_PieceMaskType.centerA;
                    maskOffset = cc.v3(maskSize.width + this.pieceOffset, 0);
                    contentOffset = cc.v3(-maskSize.width * i, -maskSize.width * j - this.pieceOffset * 4);
                } else {
                    rotation = 0;
                    pieceType = E_PieceMaskType.centerB;
                    maskOffset = cc.v3(0, this.pieceOffset);
                    contentOffset = cc.v3(-maskSize.width * (j + 1) + this.pieceOffset * 3, maskSize.width * i - this.pieceOffset);
                }
                maskSf = this.centerMask;
                posType = E_PiecePosType.center;
            }
        }

        startPos.addSelf(maskOffset);
        return { startPos, pieceType, maskSf, posType, rotation, contentOffset, maskSize };
    }

    private createPiece(
        name: string, { maskSf, pieceType, rotation, startPos, contentOffset, maskSize }: I_PieceInfo
    ) {
        let pieceMask = new cc.Node().addComponent(cc.Mask);
        let bg = new cc.Node().addComponent(cc.Sprite);
        let piece = pieceMask.node.addComponent(CPiece);
        pieceMask.node.addChild(bg.node);

        //设置图片
        bg.spriteFrame = this.contentSf;
        bg.sizeMode = cc.Sprite.SizeMode.CUSTOM;
        bg.node.angle = rotation;
        bg.node.setAnchorPoint(cc.v2(0, 1));
        bg.node.setContentSize(this.bgSize);
        bg.node.setPosition(contentOffset);

        //设置mask
        if (!this.isRulse) {
            pieceMask.alphaThreshold = 0.01;
            pieceMask.type = cc.Mask.Type.IMAGE_STENCIL;
            maskSize = maskSf.getOriginalSize();
            maskSize = cc.size(maskSize.width * this.pieceScale, maskSize.height * this.pieceScale);
        }

        pieceMask.spriteFrame = maskSf;
        pieceMask.node.angle = -rotation;
        pieceMask.node[MASK_TYPE] = pieceType;
        pieceMask.node.setAnchorPoint(cc.v2(0, 1));
        pieceMask.node.setPosition(startPos);
        pieceMask.node.setContentSize(maskSize);

        //设置碎片
        piece.tempDragNode = this.tempDragNode;
        piece.beforDragNode = this.contentNode;
        piece.node.name = name;
        piece.indexName = name;

        //测试
        if (this.isOpenTest && this.testAnchoNode) {
            pieceMask.enabled = false;
            pieceMask.node.opacity = 155;
            pieceMask.node.addChild(cc.instantiate(this.testAnchoNode));
            pieceMask.node.children[1].setPosition(cc.v3(0, 0));
            pieceMask.node.children[1].color = cc.Color.WHITE;
        }

        return pieceMask.node;
    }

    /**随机打乱位置 */
    private randomPosition() {
        let nameList = [...this.pieceDragMap.keys()];
        for (let [curName, curPiece] of this.pieceDragMap.entries()) {
            let swapName = nameList[cc[`math`].randomRangeInt(0, nameList.length)];
            let swapTarget = this.pieceDragMap.get(swapName);

            //状态-位置
            this.swapState(curName, curPiece, swapName, swapTarget);
            swapTarget.node.setPosition(this.pieceStartPosMap.get(curName));
            curPiece.node.setPosition(this.pieceStartPosMap.get(swapName));
        }
    }

    /**测试碎片 */
    private testPos(pieceType: E_PieceMaskType, posType: E_PiecePosType) {
        // if (
        //     posType != E_PiecePosType.left
        //     &&
        //     posType != E_PiecePosType.right
        //     &&
        //     posType != E_PiecePosType.up
        //     &&
        //     posType != E_PiecePosType.down
        //     &&
        //     posType != E_PiecePosType.left_down
        //     &&
        //     posType != E_PiecePosType.right_down
        //     &&
        //     posType != E_PiecePosType.right_up
        //     &&
        //     posType != E_PiecePosType.left_up
        //     &&
        //     pieceType != E_PieceMaskType.centerB
        //     &&
        //     pieceType != E_PieceMaskType.centerA
        // ) return true;
        return false;
    }

    /**
     * 开始点击碎片
     */
    onPieceTouchStartCallback(event: cc.Touch) {
        let curNode = (event as unknown as cc.Event).target as cc.Node;
        //把碎片移动到临时的移动节点

        // let pos = cc.v3(Vec2s.converNodeToOtherNodeSpaceAR(this.node, this.tempDragNode));
        // this.node.parent = this.tempDragNode;
        // this.node.setPosition(pos);

        let touchRotation = this.pieceInfoMap.get(curNode.name).rotation;
        touchRotation = 0;
        (cc.tween(curNode) as cc.Tween).to(this.moveSpeed + 0.3, { scale: 1, angle: -touchRotation }, cc.easeSineOut()).start();
    }


    onPieceTouchMove(event: cc.Touch) {
        this.node.x += event.getDelta().x;
        this.node.y += event.getDelta().y;
    }

    /**
     * 松开回调
     */
    onPieceTouchEndCallback(event: cc.Touch) {
        let curNode = (event as unknown as cc.Event).target as cc.Node;
        let pos = cc.v3(event.getLocation());
        //把碎片移动回原来的容器节点
        // let pos = cc.v3(Vec2s.converNodeToOtherNodeSpaceAR(curNode, this.beforDragNode));
        // curNode.parent = this.beforDragNode;
        // curNode.setPosition(pos);

        let curPiece = curNode.getComponent(CPiece);//当前点击的碎片
        let curName = curNode.name;

        //获取根据地图的位置目标距离
        /**获取最近区域的名字 */
        let findTarget = this.isFindAround ? this.findLatelyTarget(pos, this.getPieceStartPos(curName)) : this.findRightTarget(pos, curName);
        let { targetName, targetPos, isFind } = findTarget;
        let targetPiece = this.getPrieceNode(targetName);

        return new Promise<void>(resolve => {
            if (targetPos && isFind) {
                //把目标碎片移动到原来选中碎片的区域
                let curPos = this.getPieceStartPos(curName);
                this.swapState(curName, curPiece, targetName || curName, targetPiece);

                if (targetName != curName && targetPiece) {
                    //目标碎片移动
                    (cc.tween(targetPiece.node) as cc.Tween).to(this.moveSpeed, { position: curPos }, cc.easeSineOut()).start();
                }

                //选中碎片移动到目标
                (cc.tween(curNode) as cc.Tween).to(this.moveSpeed, { position: targetPos, angle: this.pieceScale }, cc.easeSineOut()).call(() => {
                    if (!this.checkIsWin())
                        resolve();
                }).start();
            } else {//选中碎片移动回原位
                let backPos = this.isRulse ? this.getPieceStartPos(curName) : this.tempPosMap.get(curName);
                curNode.stopAllActions();
                (cc.tween(curNode) as cc.Tween).to(this.moveSpeed, { position: backPos, scale: this.tempScale, angle: 0 }, cc.easeSineOut()).call(() => {
                    // curNode.parent = this.contentNode;
                    resolve();
                }).start();
            }
        });
    }

    /**交换状态 */
    private swapState(swapNameA: string, swapPieceA: CPiece, swapNameB: string, swapPieceB: CPiece) {
        swapPieceA.node.name = swapNameB;
        if (swapPieceB)
            swapPieceB.node.name = swapNameA;
        this.pieceDragMap.set(swapNameA, swapPieceB);
        this.pieceDragMap.set(swapNameB, swapPieceA);
    }

    /**
     * 根据当前位置查找目前最近的碎片位置
     * @param pos 当前位置
     * @param targetPos 目标位置
     */
    private findLatelyTarget(pos: cc.Vec3, targetPos: cc.Vec3) {
        let targetName: string;
        let len: number = cc.winSize.width;
        let isFind = false;
        this.pieceStartPosMap.forEach((pieceStartPos, name) => {
            let dis = this.contentNode.convertToWorldSpaceAR(pieceStartPos).sub(pos).mag();
            if (dis < len) {
                len = dis;
                targetPos = pieceStartPos;
                targetName = name;
                isFind = true;
            }
        });
        return { targetName, targetPos, isFind };
    }

    /**
     * 根据碎片和位置，并判断距离是否返回目标
     * @param pos 当前位置
     * @param targetName 碎片的名字
     */
    private findRightTarget(pos: cc.Vec3, targetName: string) {
        let rightPieceNode = this.rightPieceMap.get(targetName).node;
        let targetPos = rightPieceNode.convertToWorldSpaceAR(cc.v3(0, 0, 0));
        let isNearby = pos.fuzzyEquals(targetPos, this.findDistence);
        let isFind = true;
        if (!isNearby) {
            //原本位置
            targetPos = this.tempPosMap.get(targetName);
            isFind = false;
        } else {
            targetPos = this.tempDragNode.convertToNodeSpaceAR(targetPos);
        }

        return { targetName, targetPos, isFind };
    }

    /**检查是否完成拼图 */
    checkIsWin() {
        let isWin = [...this.pieceDragMap.entries()].every(([name, piece]) => name == piece.indexName);
        if (!isWin) {
            cc.log(`拼图完成`);
            this.pieceDragMap.forEach(piece => piece.isCanClick = false);

            //显示完整的图片
            let complateSp = new cc.Node().addComponent(cc.Sprite);
            this.node.children[0].addChild(complateSp.node);
            complateSp.spriteFrame = this.contentSf;
            complateSp.node.setContentSize(this.bgSize);
            complateSp.node.setPosition(cc.v3(0, 0));
            complateSp.node.setAnchorPoint(cc.v2(0, 1));
        }
        return isWin;
    }

    getPrieceNode(raw: number | string, col?: number) {
        let name: string;
        if (typeof raw == "string") name = raw;
        else name = this.getName(raw, col);
        return this.pieceDragMap.get(name);
    }

    getPieceStartPos(raw: number | string, col?: number) {
        let name: string;
        if (typeof raw == "string") name = raw;
        else name = this.getName(raw, col);
        return this.pieceStartPosMap.get(name);
    }

    getName(i: number, j: number): string { return `${i}_${j}`; }

}
